package com.xiangxiao.rpan.authority.oauth;

import com.xiangxiao.rpan.authority.constant.GlobalConsts;
import com.xiangxiao.rpan.utils.GeoIpUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.core.Ordered;
import org.springframework.core.annotation.Order;
import org.springframework.http.HttpHeaders;
import org.springframework.stereotype.Component;
import org.springframework.web.filter.OncePerRequestFilter;

import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.*;

/**
 * @Projectname:
 * @Author: xiangxiao
 * @Email: 573768011@qq.com
 * @Data:2023/5/9 0:05
 */
/**
 * @Order(Ordered.HIGHEST_PRECEDENCE)
 * 最高优先级,优于其他所有的Bean在IOC容器中被执行
 * **/
@Slf4j
@Component
@Order(Ordered.HIGHEST_PRECEDENCE)
public class OauthFilter extends OncePerRequestFilter {
  /**
   * 默认的token过期时间
   */
  public static final int DEFAULT_TOKEN_EXPIRED_TIME = -1;

  public static final String METHOD_OPTIONS = "OPTIONS";
  @Override
  protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
    response.setHeader("Access-Control-Allow-Methods", "POST, GET, OPTIONS, PUT, DELETE, PATCH");
    response.setHeader("Access-Control-Max-Age", "" + DEFAULT_TOKEN_EXPIRED_TIME);
    response.setHeader("Access-Control-Allow-Headers", "x-requested-with, authorization," +
      "Origin, No-Cache, X-Requested-With, If-Modified-Since, Pragma, Last-Modified, Cache-Control, Expires, Content-Type, X-E4M-With");

    if (StringUtils.equalsIgnoreCase(METHOD_OPTIONS, request.getMethod())) {
      response.setStatus(HttpServletResponse.SC_OK);
    } else {
      String ip = GeoIpUtil.getIpAddress(request);

      HookRequestWrapper requestWrapper = new HookRequestWrapper(request);
      requestWrapper.hookHeader(GlobalConsts.AUTHORIZATION_KEY, HttpHeaders.AUTHORIZATION);
      requestWrapper.addParameter(GlobalConsts.IP, new String[]{ip});
      filterChain.doFilter(requestWrapper, response);
    }
  }


  /**
   * 主要是为了解决Oauth2 某些header的替换问题,以及实现重复利用ServletInputStream的流
   * **/
  public class HookRequestWrapper extends HttpServletRequestWrapper {

    private Map<String, Enumeration<String>> customHeaderMap = new HashMap<>();

    private Map<String, String> hookKeyMap = new HashMap<>();

    /**
     * 动态添加参数
     */
    private Map<String, String[]> hookParameterMap = new HashMap<>();

    private Set<String> nameSet = new HashSet<>();

    public HookRequestWrapper(HttpServletRequest request) {
      super(request);
    }

    @Override
    public Enumeration<String> getHeaders(String name) {

      //优先获取自定义的
      if (customHeaderMap.containsKey(name)) {
        return customHeaderMap.get(name) == null ? new Vector<String>().elements() : customHeaderMap.get(name);
      }

      //其次获取hook的
      if (hookKeyMap.containsKey(name)) {
        return getHeaders(hookKeyMap.get(name));
      }

      return super.getHeaders(name);
    }

    @Override
    public String getHeader(String name) {
      Enumeration<String> headers = getHeaders(name);
      if (!headers.hasMoreElements()) {
        return null;
      }
      return headers.nextElement();
    }

    /**
     * 添加上自定义的Header
     *
     * @param key
     * @param value
     */
    public void addHeader(String key, String value) {
      if (StringUtils.isBlank(key) || StringUtils.isBlank(value)) {
        return;
      }

      List<String> dataList = new ArrayList<>();
      dataList.add(value);
      customHeaderMap.put(key, Collections.enumeration(dataList));
    }

    /**
     * 使用key的值代替hookKey的值
     *
     * @param key
     * @param hookKey
     */
    public void hookHeader(String key, String hookKey) {
      if (StringUtils.isBlank(key) || StringUtils.isBlank(hookKey)) {
        return;
      }
      hookKeyMap.put(hookKey, key);
    }

    @Override
    public Enumeration<String> getParameterNames() {
      Enumeration<String> parameterNames = super.getParameterNames();
      while (parameterNames.hasMoreElements()) {
        nameSet.add(parameterNames.nextElement());
      }

      return Collections.enumeration(nameSet);
    }

    @Override
    public Map<String, String[]> getParameterMap() {
      Map<String, String[]> parameterMap = super.getParameterMap();
      for (Map.Entry<String, String[]> entry : hookParameterMap.entrySet()) {
        String key = entry.getKey();
        if (parameterMap.containsKey(key)) {
          String[] oldData = parameterMap.get(key);
          String[] hookData = hookParameterMap.get(key);
          if (hookData != null) {
            if (oldData == null) {
              parameterMap.put(key, hookData);
            } else {
              String[] newData = new String[oldData.length + hookData.length];
              System.arraycopy(oldData, 0, newData, 0, oldData.length);
              System.arraycopy(hookData, 0, newData, oldData.length, newData.length);
              parameterMap.put(key, newData);
            }
          }
        }
      }
      return parameterMap;
    }

    public void addParameter(String key, String[] values) {
      nameSet.add(key);
      hookParameterMap.put(key, values);
    }
  }
}
